package tp.pr4;


import java.util.Collections;
import java.util.Stack;

import tp.pr4.City;
import tp.pr4.Direction;
import tp.pr4.Place;
import tp.pr4.RobotEngine;
import tp.pr4.Rotation;
import tp.pr4.instructions.Instruction;
import tp.pr4.instructions.MoveInstruction;
import tp.pr4.instructions.OperateInstruction;
import tp.pr4.instructions.PickInstruction;
import tp.pr4.instructions.TurnInstruction;
import tp.pr4.instructions.UndoableInstruction;

public class AutoPlayer3 extends RobotEngine {

	/**
	 * Constructor for the parent class
	 * @param cityMap
	 * @param initialPlace
	 * @param direction
	 */
	public AutoPlayer3(City cityMap, Place initialPlace, Direction direction) {
		super(cityMap, initialPlace, direction);
	}
	

	@Override
	public void requestStart() {
		super.requestStart();
		this.play(0);
		super.onEngineOff(); //avisamos de que hemos acabado con lo que sea, bueno o malo
	}

	/**
	 * Function that given a state for the robot tries to get to the end from there
	 * @throws ArrivalAtShipException when it reaches the end
	 */
	private void play(int moves){
		//sleep a bit to make actions visible on the screen
//		try {
//			Thread.sleep(1); //sleep some time for the updates to show properly on the displays
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
		//try to move
		
		if (hasFoundSolution() && currentList.size() >= bestList.size() - 1){
			this.undoInstruction();
			return;
		}
		
		
		if (this.gps.getHeadingStreet() != null &&
				this.gps.getHeadingStreet().nextPlace(this.gps.getCurrentPlace()).isSpaceship()){
				Collections.copy(this.currentList, this.bestList);
				return;
		}
		else if (this.communicateAndCheck(new MoveInstruction()) == STATUS_EXECUTED){
					play(moves + 1);
		}
		//try to pick an item
		for (int i = this.pocket.numberOfItems()-1; i >= 0 ; i--)
			if (this.communicateAndCheck(new OperateInstruction(this.pocket.getId(i))) == STATUS_EXECUTED)
				play(moves + 1);
		//try to use the picked item
		for (int i = this.gps.getCurrentPlace().getContainer().numberOfItems()-1; i >= 0; i--)
			if (this.communicateAndCheck(new PickInstruction(this.gps.getCurrentPlace().getContainer().getId(i))) == STATUS_EXECUTED)
				play(moves + 1);
		//try to turn in a direction
		if (this.communicateAndCheck(new TurnInstruction(Rotation.LEFT)) == STATUS_EXECUTED)
			play(moves + 1);
		//try the other direction
		if (this.communicateAndCheck(new TurnInstruction(Rotation.RIGHT)) == STATUS_EXECUTED)
			play(moves + 1);
	
		//everything failed. Undo and try to start over from last succesfull point
		this.undoInstruction();
	}
	
	boolean hasFoundSolution(){
		return bestList != null;
	}
	
	/**
	 * Communicates an undoable instruction to the robot returning different values according to the result. 
	 * @param i Instruction to communicate
	 * @return 	AutoPlayer.STATUS_ISOVER if the robot can't perform actions <br>
	 * 			AutoPlayer.STATUS_FAILED if the instruction failed <br>
	 * 			AutoPlayer.STATUS_EXECUTED if the instruction was correctly executed
	 * @throws ArrivalAtShipException when the robot arrives at its final destination
	 */
	private int communicateAndCheck(UndoableInstruction i) {
		//if the robot is already out of fuel, return
		if (this.isOver())
			return STATUS_ISOVER;
		//configure context and try to execute it
		i.configureContext(this, this.gps, this.pocket);
		if (this.executeInstruction(i)){ //if it executes, add to the undo list
			this.undoManager.addInstruction(i);
			this.currentList.add(i);
			return STATUS_EXECUTED;
		}else							//if not, return 
			return STATUS_FAILED;
	}
	private Stack<Instruction> currentList  = new Stack<Instruction>();
	private Stack<Instruction> bestList = null;
	
	private static final int STATUS_EXECUTED = 0;
	private static final int STATUS_FAILED = 1;
	private static final int STATUS_ISOVER = 2;
}



